package org.squabble.domain;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.MapKey;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Version;

import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.GenericGenerator;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

@Entity(name = "ACCOUNT")
@Table(name = "ACCOUNT")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@JsonIgnoreProperties({"accountProperties", "privileges", "authorities", "password", "salt"})
public class Account implements UserDetails, Serializable, Comparable<Account> {

	/**
	 * 
	 */
	private static final long serialVersionUID = 4779423375627905022L;

	@Id
	@Column(name = "ID")
	@GenericGenerator(name = "account_seq", strategy = "increment")
	@GeneratedValue(generator = "account_seq")
	protected Long id;

	@Column(name = "NAME", nullable = false, unique = true, length = 25)
	protected String name;

	@Column(name = "EMAIL", nullable = false, unique = true, length = 255)
	protected String email;

	@Column(name = "PASSWORD", nullable = false, length = 255)
	protected String password;

	@Column(name = "TITLE", nullable = true, length = 255)
	protected String title;

	@Column(name = "SOURCE_DATE", nullable = false)
	protected Date sourceDate = new Date();

	@Column(name = "LAST_LOGIN_DATE", nullable = true)
	protected Date lastLogin;

	@Column(name = "ACCOUNT_EXPIRE_DATE", nullable = true)
	protected Date accountExpireDate;

	@Column(name = "PASSWORD_EXPIRE_DATE", nullable = true)
	protected Date passwordExpireDate;

	@Column(name = "ENABLED", nullable = false)
	protected boolean enabled = true;

	@Column(name = "LOCKED", nullable = false)
	protected boolean locked = false;

	@Column(name = "SALT", nullable = false, length = 10)
	protected String salt;

	@Column(name = "VALIDATION_CODE", nullable = true, length = 32)
	protected String validationCode;

	@Version
	@Column(name = "VERSION")
	protected Long version;

	@ManyToMany(targetEntity = Privilege.class, fetch = FetchType.LAZY)
	@JoinTable(name = "ACCOUNT_PRIVILEGE", joinColumns = @JoinColumn(name = "ACCOUNT_ID"), inverseJoinColumns = @JoinColumn(name = "PRIVILEGE_ID"))
	@Cascade(value = org.hibernate.annotations.CascadeType.SAVE_UPDATE)
	private Set<Privilege> privileges = new LinkedHashSet<Privilege>();

	@OneToMany(targetEntity = AccountProperty.class, fetch = FetchType.LAZY, orphanRemoval = true)
	@JoinColumn(name = "ACCOUNT_ID")
	@Cascade(value = org.hibernate.annotations.CascadeType.SAVE_UPDATE)
	@MapKey(name = "name")
	private Map<String, AccountProperty> accountProperties = new HashMap<String, AccountProperty>();

	public Long getId() {
		return id;
	}

	public void setId(Long accountId) {
		this.id = accountId;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public Date getSourceDate() {
		return sourceDate;
	}

	public void setSourceDate(Date sourceDate) {
		this.sourceDate = sourceDate;
	}

	public Date getLastLogin() {
		return lastLogin;
	}

	public void setLastLogin(Date lastLogin) {
		this.lastLogin = lastLogin;
	}

	public Date getAccountExpireDate() {
		return accountExpireDate;
	}

	public void setAccountExpireDate(Date accountExpireDate) {
		this.accountExpireDate = accountExpireDate;
	}

	public Date getPasswordExpireDate() {
		return passwordExpireDate;
	}

	public void setPasswordExpireDate(Date passwordExpireDate) {
		this.passwordExpireDate = passwordExpireDate;
	}

	public Set<Privilege> getPrivileges() {
		return privileges;
	}

	public void setPrivileges(Set<Privilege> privileges) {
		this.privileges = privileges;
	}

	@Override
	public boolean isEnabled() {
		return enabled;
	}

	public void setEnabled(boolean enabled) {
		this.enabled = enabled;
	}

	public boolean isLocked() {
		return locked;
	}

	public void setLocked(boolean locked) {
		this.locked = locked;
	}

	public String getSalt() {
		return salt;
	}

	public void setSalt(String salt) {
		this.salt = salt;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getUsername() {
		return email;
	}

	public String getValidationCode() {
		return validationCode;
	}

	public void setValidationCode(String validationCode) {
		this.validationCode = validationCode;
	}

	public Long getVersion() {
		return version;
	}

	public void setVersion(Long version) {
		this.version = version;
	}

	public Map<String, AccountProperty> getAccountProperties() {
		return accountProperties;
	}

	public void setAccountProperties(Map<String, AccountProperty> properties) {
		this.accountProperties = properties;
	}

	@Override
	public Collection<GrantedAuthority> getAuthorities() {
		return new ArrayList<GrantedAuthority>(privileges);
	}

	@Override
	public boolean isAccountNonExpired() {
		if (accountExpireDate == null)
			return true;
		if (accountExpireDate.after(new Date()))
			return true;
		return false;
	}

	@Override
	public boolean isAccountNonLocked() {
		return !locked;
	}

	@Override
	public boolean isCredentialsNonExpired() {
		if (passwordExpireDate == null)
			return true;
		if (passwordExpireDate.after(new Date()))
			return true;
		return false;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof Account))
			return false;
		Account other = (Account) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}

	/**
	 * Constructs a <code>String</code> with all attributes in name = value
	 * format.
	 * 
	 * @return a <code>String</code> representation of this object.
	 */
	public String toString() {
		final String TAB = "    ";

		StringBuffer retValue = new StringBuffer();

		retValue.append("Account ( ").append(super.toString()).append(TAB)
				.append("id = ").append(this.id).append(TAB).append("name = ")
				.append(this.name).append(TAB).append("email = ").append(
						this.email).append(TAB).append("password = ").append(
						this.password).append(TAB).append("sourceDate = ")
				.append(this.sourceDate).append(TAB).append("lastLogin = ")
				.append(this.lastLogin).append(TAB).append(
						"accountExpireDate = ").append(this.accountExpireDate)
				.append(TAB).append("passwordExpireDate = ").append(
						this.passwordExpireDate).append(TAB).append(
						"enabled = ").append(this.enabled).append(TAB).append(
						"locked = ").append(this.locked).append(TAB).append(
						"salt = ").append(this.salt).append(TAB).append(
						"version = ").append(this.version).append(TAB).append(
						" )");

		return retValue.toString();
	}

	@Override
	public int compareTo(Account o) {
		// TODO Auto-generated method stub
		return 0;
	}

}
